﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.IO;
using System.Collections;

namespace PerformanceDude.MSBuildShellExtension
{
    public class Solution
    {
        //internal class SolutionParser
        //Name: Microsoft.Build.Construction.SolutionParser
        //Assembly: Microsoft.Build, Version=4.0.0.0

        static readonly Type _SolutionParser;

        static readonly PropertyInfo _solutionReader;
        static readonly PropertyInfo _projects;
        static readonly PropertyInfo _SolutionConfigurations;
        static readonly PropertyInfo _Version;

        static readonly MethodInfo _parseSolution;
        static readonly MethodInfo _ParseSolutionConfigurations;

        static Solution()
        {
            _SolutionParser = Type.GetType("Microsoft.Build.Construction.SolutionParser, Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false, false);
            if (_SolutionParser != null)
            {
                _solutionReader = _SolutionParser.GetProperty("SolutionReader", BindingFlags.NonPublic | BindingFlags.Instance);
                _projects = _SolutionParser.GetProperty("Projects", BindingFlags.NonPublic | BindingFlags.Instance);
                _parseSolution = _SolutionParser.GetMethod("ParseSolution", BindingFlags.NonPublic | BindingFlags.Instance);
                _ParseSolutionConfigurations = _SolutionParser.GetMethod("ParseSolutionConfigurations", BindingFlags.NonPublic | BindingFlags.Instance);
                
                _projects = _SolutionParser.GetProperty("Projects", BindingFlags.NonPublic | BindingFlags.Instance);
                _SolutionConfigurations = _SolutionParser.GetProperty("SolutionConfigurations", BindingFlags.NonPublic | BindingFlags.Instance);
                _Version = _SolutionParser.GetProperty("Version", BindingFlags.NonPublic | BindingFlags.Instance);
            }
        }

        public List<SolutionProject> Projects { get; private set; }
        public List<SolutionConfiguration> SolutionConfigurations { get; private set; }
        public int Version { get; private set; }

        public Solution(string solutionFileName)
        {
            if (_SolutionParser == null)
            {
                throw new InvalidOperationException("Can not find type 'Microsoft.Build.Construction.SolutionParser' are you missing a assembly reference to 'Microsoft.Build.dll'?");
            }
            
            var solutionParser = _SolutionParser.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).First().Invoke(null);
            using (var streamReader = new StreamReader(solutionFileName))
            {
                _solutionReader.SetValue(solutionParser, streamReader, null);
                _parseSolution.Invoke(solutionParser, null);
                _ParseSolutionConfigurations.Invoke(solutionParser, null);
            }
            
            var projects = new List<SolutionProject>();
            var array = (Array)_projects.GetValue(solutionParser, null);
            for (int i = 0; i < array.Length; i++)
            {
                projects.Add(new SolutionProject(array.GetValue(i)));
            }
            this.Projects = projects;

            var solutionConfigurations = new List<SolutionConfiguration>();
            var solutionConfigurationsArray = (IList)_SolutionConfigurations.GetValue(solutionParser, null);
            for (int i = 0; i < solutionConfigurationsArray.Count; i++)
            {
                solutionConfigurations.Add(new SolutionConfiguration(solutionConfigurationsArray[i]));
            }
            this.SolutionConfigurations = solutionConfigurations;

            this.Version = (int)_Version.GetValue(solutionParser, null);
        }
    }
}
